﻿using System;
using System.Runtime.InteropServices;

namespace Btstack
{
    public class Hci
    {
        public enum HciPowerMode
        {
            Off,
            On,
            Sleep
        }

        public enum HciState
        {
            Off,
            Initializing,
            Working,
            Halting,
            Sleeping,
            Falling_asleep
        }

        public enum HciDumpFormat
        {
            Bluez,
            Packetlogger,
            Stdout
        }

        public const int HCI_DUMP_LOG_LEVEL_DEBUG = 0;
        public const int HCI_DUMP_LOG_LEVEL_INFO = 1;
        public const int HCI_DUMP_LOG_LEVEL_ERROR = 2;

        public const ushort HCI_CON_HANDLE_INVALID = 0xffff;

        // packet types - used in BTstack and over the H4 UART interface
        public const byte HCI_COMMAND_DATA_PACKET = 0x01;
        public const byte HCI_ACL_DATA_PACKET = 0x02;
        public const byte HCI_SCO_DATA_PACKET = 0x03;
        public const byte HCI_EVENT_PACKET = 0x04;

        // Events from host controller to host
        public const byte HCI_EVENT_INQUIRY_COMPLETE = 0x01;
        public const byte HCI_EVENT_INQUIRY_RESULT = 0x02;
        public const byte HCI_EVENT_CONNECTION_COMPLETE = 0x03;
        public const byte HCI_EVENT_CONNECTION_REQUEST = 0x04;
        public const byte HCI_EVENT_DISCONNECTION_COMPLETE = 0x05;
        public const byte HCI_EVENT_AUTHENTICATION_COMPLETE = 0x06;
        public const byte HCI_EVENT_AUTHENTICATION_COMPLETE_EVENT = HCI_EVENT_AUTHENTICATION_COMPLETE;
        public const byte HCI_EVENT_REMOTE_NAME_REQUEST_COMPLETE = 0x07;
        public const byte HCI_EVENT_ENCRYPTION_CHANGE = 0x08;
        public const byte HCI_EVENT_CHANGE_CONNECTION_LINK_KEY_COMPLETE = 0x09;
        public const byte HCI_EVENT_MASTER_LINK_KEY_COMPLETE = 0x0A;
        public const byte HCI_EVENT_READ_REMOTE_SUPPORTED_FEATURES_COMPLETE = 0x0B;
        public const byte HCI_EVENT_READ_REMOTE_VERSION_INFORMATION_COMPLETE = 0x0C;
        public const byte HCI_EVENT_QOS_SETUP_COMPLETE = 0x0D;
        public const byte HCI_EVENT_COMMAND_COMPLETE = 0x0E;
        public const byte HCI_EVENT_COMMAND_STATUS = 0x0F;
        public const byte HCI_EVENT_HARDWARE_ERROR = 0x10;
        public const byte HCI_EVENT_FLUSH_OCCURRED = 0x11;
        public const byte HCI_EVENT_ROLE_CHANGE = 0x12;
        public const byte HCI_EVENT_NUMBER_OF_COMPLETED_PACKETS = 0x13;
        public const byte HCI_EVENT_MODE_CHANGE = 0x14;
        public const byte HCI_EVENT_RETURN_LINK_KEYS = 0x15;
        public const byte HCI_EVENT_PIN_CODE_REQUEST = 0x16;
        public const byte HCI_EVENT_LINK_KEY_REQUEST = 0x17;
        public const byte HCI_EVENT_LINK_KEY_NOTIFICATION = 0x18;
        public const byte HCI_EVENT_DATA_BUFFER_OVERFLOW = 0x1A;
        public const byte HCI_EVENT_MAX_SLOTS_CHANGED = 0x1B;
        public const byte HCI_EVENT_READ_CLOCK_OFFSET_COMPLETE = 0x1C;
        public const byte HCI_EVENT_CONNECTION_PACKET_TYPE_CHANGED = 0x1D;
        public const byte HCI_EVENT_INQUIRY_RESULT_WITH_RSSI = 0x22;
        public const byte HCI_EVENT_SYNCHRONOUS_CONNECTION_COMPLETE = 0x2C;
        public const byte HCI_EVENT_EXTENDED_INQUIRY_RESPONSE = 0x2F;
        public const byte HCI_EVENT_ENCRYPTION_KEY_REFRESH_COMPLETE = 0x30;
        public const byte HCI_EVENT_IO_CAPABILITY_REQUEST = 0x31;
        public const byte HCI_EVENT_IO_CAPABILITY_RESPONSE = 0x32;
        public const byte HCI_EVENT_USER_CONFIRMATION_REQUEST = 0x33;
        public const byte HCI_EVENT_USER_PASSKEY_REQUEST = 0x34;
        public const byte HCI_EVENT_REMOTE_OOB_DATA_REQUEST = 0x35;
        public const byte HCI_EVENT_SIMPLE_PAIRING_COMPLETE = 0x36;
        public const byte HCI_EVENT_LE_META = 0x3E;
        public const byte HCI_EVENT_VENDOR_SPECIFIC = 0xFF;
        public const byte HCI_SUBEVENT_LE_CONNECTION_COMPLETE = 0x01;
        public const byte HCI_SUBEVENT_LE_ADVERTISING_REPORT = 0x02;
        public const byte HCI_SUBEVENT_LE_CONNECTION_UPDATE_COMPLETE = 0x03;
        public const byte HCI_SUBEVENT_LE_READ_REMOTE_USED_FEATURES_COMPLETE = 0x04;
        public const byte HCI_SUBEVENT_LE_LONG_TERM_KEY_REQUEST = 0x05;
        public const byte HCI_SUBEVENT_LE_REMOTE_CONNECTION_PARAMETER_REQUEST = 0x06;
        public const byte HCI_SUBEVENT_LE_DATA_LENGTH_CHANGE = 0x07;
        public const byte HCI_SUBEVENT_LE_READ_LOCAL_P256_PUBLIC_KEY_COMPLETE = 0x08;
        public const byte HCI_SUBEVENT_LE_GENERATE_DHKEY_COMPLETE = 0x09;
        public const byte HCI_SUBEVENT_LE_ENHANCED_CONNECTION_COMPLETE = 0x0A;
        public const byte HCI_SUBEVENT_LE_DIRECT_ADVERTISING_REPORT = 0x0B;

        [DllImport("btstack-c.dll", EntryPoint = "hci_power_control")]
        public static extern int PowerControl(HciPowerMode mode);

        [DllImport("btstack-c.dll", EntryPoint = "hci_close")]
        public static extern int Close();

        [DllImport("btstack-c.dll", EntryPoint = "hci_transport_usb_instance")]
        public static extern IntPtr TransportUsbInstance();

        [DllImport("btstack-c.dll", EntryPoint = "hci_init")]
        public static extern void Init(IntPtr transport, IntPtr config);

        [DllImport("btstack-c.dll", EntryPoint = "hci_add_event_handler")]
        private static extern void AddEventHandler(IntPtr CallbackHandler);

        [DllImport("btstack-c.dll", EntryPoint = "hci_dump_open")]
        public static extern void DumpOpen(string filename, HciDumpFormat format);

        [DllImport("btstack-c.dll", EntryPoint = "hci_dump_enable_log_level")]
        public static extern void DumpEnableLogLevel(int log_level, bool enable);

        public static void AddEventHandler(Action<byte, ushort, byte[]> callback)
        {
            void CallbackLowlevel(byte type, ushort channel, IntPtr PacketPtr, ushort size)
            {
                byte[] packet = new byte[size];
                Marshal.Copy(PacketPtr, packet, 0, size);
                callback(type, channel, packet);
            }

            IntPtr PacketCallbackRegistrationPtr = Marshal.AllocHGlobal(Marshal.SizeOf<IntPtr>() * 2);
            IntPtr CallbackPtr = Marshal.GetFunctionPointerForDelegate((Btstack.PacketHandlerType)CallbackLowlevel);
            Marshal.WriteIntPtr(PacketCallbackRegistrationPtr, Marshal.SizeOf<IntPtr>(), CallbackPtr);
            AddEventHandler(PacketCallbackRegistrationPtr);
        }

        public static byte EventPacketGetType(byte[] packet)
        {
            return packet[0];
        }

        public static byte EventLeMetaGetSubeventCode(byte[] packet)
        {
            return packet[2];
        }

        public static ushort SubeventLeConnectionCompleteGetConnectionHandle(byte[] packet)
        {
            return BitConverter.ToUInt16(packet, 4);
        }

        public static ushort SubeventLeConnectionCompleteGetConnInterval(byte[] packet)
        {
            return BitConverter.ToUInt16(packet, 14);
        }

        public static ushort SubeventLeConnectionCompleteGetConnLatency(byte[] packet)
        {
            return BitConverter.ToUInt16(packet, 16);
        }
    }
}
